/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * http//www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is OntologyIndexer.
 *
 * The Initial Developer of the Original Code is University of Roma Tor Vergata.
 * Portions created by University of Roma Tor Vergata are Copyright (C) 2006.
 * All Rights Reserved.
 *
 * OntologyIndexer was developed by the Artificial Intelligence Research Group
 * (ai-nlp.info.uniroma2.it) at the University of Roma Tor Vergata
 * Current information about OntologyIndexer can be obtained at 
 * http//ai-nlp.info.uniroma2.it/software/...
 *
 */

/*
 * Contributor(s): Armando Stellato stellato@info.uniroma2.it
 */

package it.uniroma2.art.owlart.langsearch;

import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.langsearch.index.OWLIndexManager;
import it.uniroma2.art.owlart.langsearch.index.OntoTypeNotIndexableException;
import it.uniroma2.art.owlart.langsearch.index.config.IndexingConfiguration;
import it.uniroma2.art.owlart.langsearch.search.IndexAccessException;
import it.uniroma2.art.owlart.langsearch.search.OntIndexSearcher;
import it.uniroma2.art.owlart.langsearch.search.RDFSearchResult;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.models.OWLModel;
import it.uniroma2.art.owlart.navigation.ARTURIResourceIterator;
import it.uniroma2.art.owlart.vocabulary.RDFResourceRolesEnum;
import it.uniroma2.art.owlart.vocabulary.RDFS;

import java.io.IOException;
import java.util.Collection;

import org.apache.lucene.queryParser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The main class of this library.<br/>
 * It allows for the creation of a multilingual index associated to a given {@link OWLModel}, for its
 * maintenance, and to query its content. <br/>
 * <br/>
 * <em>USAGE:</em><br/>
 * <ul>
 * <li>invoke the constructor, by specifying proper languages to be indexed; hint:
 * {@link #OntologyIndexer(OWLModel, String, String...)} is the easiest to use and will cover most of common
 * cases</li>
 * <li>invoke either:</li>
 * <ul>
 * <li>{@link #buildIndex(boolean, VocabularyTypesEnum...)} or</li>
 * <li>build any number of custom indexes with
 * {@link #buildCustomIndex(String, ARTURIResourceIterator, boolean)} or
 * 
 * {@link #buildCustomIndex(String, ARTURIResourceIterator, ARTURIResource, boolean)}</li>
 * </ul>
 * </ul> Note: not clear if and when {@link #initializeStandardIndexWriters(VocabularyTypesEnum...)} or
 * {@link #initializeCustomIndexWriters(Collection)} should be used<br/>
 * <br/>
 * 
 * @author Armando Stellato <stellato@info.uniroma2.it>
 */
public class OWLSearchEngine {

	/**
	 * The OWL Art API {@link OWLModel} which is used to access the ontology
	 */
	public OWLModel m;

	protected OWLIndexManager ontIndexManager;
	protected OntIndexSearcher ontSearcher;
	// URILangs is used only for search, as all localnames and URIs are always stored in a generic indexed,
	// which may be searched just in case localnames are expressed in some natural language
	protected Collection<String> URILangs;
	protected boolean useLanguageSpecificAnalyzers;

	public static RDFResourceRolesEnum[] allTypes;

	static {
		allTypes = new RDFResourceRolesEnum[3];
		allTypes[0] = RDFResourceRolesEnum.cls;
		allTypes[1] = RDFResourceRolesEnum.property;
		allTypes[2] = RDFResourceRolesEnum.individual;
	}

	protected static Logger logger = LoggerFactory.getLogger(OWLSearchEngine.class);

	/**
	 * @param m
	 *            the {@link OWLModel} which is being indexed
	 * @param IndexDir
	 *            the root dir for the index
	 * @param URILangs
	 *            a set of languages associated to the local names of URI resources. That is, we expect that
	 *            the resources contained in OWLModel may have their local names expressed according to one of
	 *            the languages in this collection. This collection usually contains one single language,
	 *            though may contain more than one (if multiple ontologies are imported in the model) as well
	 *            as being empty (in those cases where unreadable codes are used as names for resources)
	 * @param languages
	 *            the set of languages which are being indexed. By default, values of property rdf:label for
	 *            each resource are being indexed
	 * @param langAnalysis
	 *            use specific analyzers (when available) for each of the specified languages
	 * 
	 */
	public OWLSearchEngine(OWLModel m, String IndexDir, IndexingConfiguration idxConfig) {
		// log.setLevel(Level.DEBUG);
		this.m = m;
		ontIndexManager = new OWLIndexManager(m, IndexDir, idxConfig);
		URILangs = idxConfig.getLanguagesOfURILocalNames();
	}

	/**
	 * If <code>true</code>, also binding derived from inferred triples are being indexed
	 * 
	 * @return
	 */
	public boolean isInference() {
		return ontIndexManager.isInference();
	}

	/**
	 * sets the inference level. If <code>true</code>, also binding derived from inferred triples are being
	 * indexed
	 * 
	 * @param inference
	 */
	public void setInference(boolean inference) {
		ontIndexManager.setInference(inference);
	}

	/**
	 * builds the index for resources of type <code>VocabularyTypesEnum</code> contained in the managed
	 * OWLModel. Index is built by indexing the values of all rdf:labels associated to the above resources
	 * which are marked as having an ISO language which is included in the list of languages managed by this
	 * ontology indexer
	 * 
	 * @param create
	 * @param enums
	 * @return
	 * @throws ModelAccessException
	 * @throws IOException
	 * @throws OntoTypeNotIndexableException
	 */
	public OntIndexSearcher buildIndex(boolean create, RDFResourceRolesEnum... enums)
			throws ModelAccessException, IOException, OntoTypeNotIndexableException {

		ontIndexManager.buildIndex(create, enums);
		OntIndexSearcher searcher = new OntIndexSearcher(ontIndexManager, URILangs);
		ontSearcher = searcher;
		return searcher;
	}

	/**
	 * as for {@link #buildCustomIndex(String, ARTURIResourceIterator, ARTURIResource, boolean)} with third
	 * predicate = rdfs:label
	 * 
	 * @param idxID
	 * @param resourcesIterator
	 * @param create
	 * @return
	 * @throws ModelAccessException
	 * @throws IOException
	 */
	public OntIndexSearcher buildCustomIndex(String idxID, ARTURIResourceIterator resourcesIterator,
			boolean create) throws ModelAccessException, IOException {
		return buildCustomIndex(idxID, resourcesIterator, RDFS.Res.LABEL, create);
	}

	/**
	 * builds a custom index, which is associated to identifier <code>idxID</code>, and which is composed by
	 * the values of all triples having:
	 * <ul>
	 * <li>elements of the resourceIterator as subject</li>
	 * <li><code>predicate</code> as predicate</li>
	 * </ul>
	 * these values are marked as having an ISO language which is included in the list of languages managed by
	 * this ontology indexer
	 * 
	 * @param idxID
	 * @param resourcesIterator
	 *            the iterator over the set of resources which are indexed
	 * @param predicate
	 *            the property of the resource the value of which will be indexed
	 * @param create
	 *            if the index is to be created or added to an existing one
	 * @return
	 * @throws ModelAccessException
	 * @throws IOException
	 */
	public OntIndexSearcher buildCustomIndex(String idxID, ARTURIResourceIterator resourcesIterator,
			ARTURIResource predicate, boolean create) throws ModelAccessException, IOException {

		ontIndexManager.buildIndex(idxID, resourcesIterator, predicate, create);
		OntIndexSearcher searcher = new OntIndexSearcher(ontIndexManager, URILangs);
		ontSearcher = searcher;
		return searcher;
	}

	/**
	 * activates a multilingual search engine for the indexed ontology. The search engine is configured upon
	 * the initialization parameters of this ontology indexer.
	 * 
	 * @return
	 * @throws IOException
	 */
	public OntIndexSearcher createOntIndexSearcher() throws IOException {
		OntIndexSearcher searcher = new OntIndexSearcher(ontIndexManager, URILangs);
		ontSearcher = searcher;
		logger.debug("ontsearcher set " + ontSearcher);
		return searcher;
	}

	/**
	 * @return the confidence threshold for returning results from the search engine
	 */
	public double getTHRESHOLD() {
		return Double.parseDouble(ontSearcher.getTHRESHOLD());
	}

	/**
	 * sets the confidence level for returning results from the search engine
	 * 
	 * @param threshold
	 */
	public void setTHRESHOLD(double threshold) {
		ontSearcher.setTHRESHOLD(Double.toString(threshold));
	}

	/**
	 * returns classes matched associated to terms by the search string <code>searchString</code>, which
	 * expressed in language <code>lang</code>
	 * 
	 * 
	 * @param searchString
	 * @param lang
	 * @param strategy
	 * @return
	 * @throws IndexAccessException
	 * @throws ParseException
	 */
	public Collection<ARTURIResource> findClasses(String searchString, String lang, int strategy)
			throws IndexAccessException, ParseException {
		logger.debug(ontSearcher + "  looking for classes");
		return ontSearcher.findClses(m, searchString, lang, strategy);
	}

	/**
	 * returns properties associated to terms matched by the search string <code>searchString</code>, which is
	 * expressed in language <code>lang</code>
	 * 
	 * @param label
	 * @param lang
	 * @param strategy
	 * @return
	 * @throws IndexAccessException
	 * @throws ParseException
	 */
	public Collection<ARTURIResource> findProperties(String label, String lang, int strategy)
			throws IndexAccessException, ParseException {
		return ontSearcher.findProperties(m, label, lang, strategy);
	}

	/**
	 * returns individuals associated to terms matched by the search string <code>searchString</code>, which
	 * is expressed in language <code>lang</code>
	 * 
	 * @param label
	 * @param lang
	 * @param strategy
	 * @return
	 * @throws IndexAccessException
	 * @throws ParseException
	 */
	public Collection<ARTURIResource> findIndividuals(String label, String lang, int strategy)
			throws IndexAccessException, ParseException {
		return ontSearcher.findIndividuals(m, label, lang, strategy);
	}

	/**
	 * returns resources associated to terms matched by the search string <code>searchString</code>, which is
	 * expressed in language <code>lang</code>. Resources are those stored according to a customized index
	 * identified by index <code>indexID</code>
	 * 
	 * @param role
	 * @param label
	 * @param lang
	 * @param strategy
	 * @return
	 * @throws IndexAccessException
	 * @throws ParseException
	 */
	public Collection<ARTURIResource> findResources(RDFResourceRolesEnum role, String label, String lang,
			int strategy) throws IndexAccessException, ParseException {
		logger.debug(ontSearcher + "  looking for classes");
		return ontSearcher.findResources(m, role.toString(), label, lang, strategy);
	}

	/**
	 * returns resources associated to terms matched by the search string <code>searchString</code>, which is
	 * expressed in language <code>lang</code>. Resources are those stored according to a customized index
	 * identified by index <code>indexID</code>
	 * 
	 * @param indexID
	 * @param label
	 * @param lang
	 * @param strategy
	 * @return
	 * @throws IndexAccessException
	 * @throws ParseException
	 */
	public Collection<ARTURIResource> findResourcesForIndex(String indexID, String label, String lang,
			int strategy) throws IndexAccessException, ParseException {
		return ontSearcher.findResources(m, indexID, label, lang, strategy);
	}

	/**
	 * returns terms matched by the search string <code>searchString</code>, which is expressed in language
	 * <code>lang</code>. Returned terms are values of rdf:label associated to resources of type
	 * <code>type</code>.
	 * 
	 * @param type
	 * @param label
	 * @param lang
	 * @param strategy
	 * @return
	 * @throws IndexAccessException
	 * @throws ParseException
	 */
	public Collection<String> findLabelsForType(RDFResourceRolesEnum type, String label, String lang,
			int strategy) throws IndexAccessException, ParseException {
		return ontSearcher.findLabels(m, type.toString(), label, lang, strategy);
	}

	/**
	 * returns terms matched by the search string <code>searchString</code>, which is expressed in language
	 * <code>lang</code>. Returned terms stored according to a customized index identified by index
	 * <code>indexID</code>
	 * 
	 * @param indexID
	 * @param label
	 * @param lang
	 * @param strategy
	 * @return
	 * @throws IndexAccessException
	 * @throws ParseException
	 */
	public Collection<String> findLabelsForIndex(String indexID, String label, String lang, int strategy)
			throws IndexAccessException, ParseException {
		return ontSearcher.findLabels(m, indexID, label, lang, strategy);
	}

	public OWLIndexManager getIndexManager() {
		return ontIndexManager;
	}

	public Collection<RDFSearchResult> findFullResourceDescription(String indexID, String label, String lang,
			int strategy) throws IndexAccessException, ParseException {
		return ontSearcher.searchFull(label, lang, indexID, strategy);
	}

	public void initializeStandardIndexWriters(RDFResourceRolesEnum... ontTypes) throws IOException {
		ontIndexManager.initializeStandardIndexWriters(ontTypes);
	}

	public void initializeCustomIndexWriters(Collection<String> idxIdentifiers) throws IOException {
		ontIndexManager.initializeCustomIndexWriters(idxIdentifiers);
	}

}
